package JavaEE.JVM;

// JDK Java开发工具包(写Java代码/编译代码 时用到的)
// JRE Java运行时环境(运行Java程序,需要的内容)
// JVM (JRE中的核心模块)

// -----JVM -> Java虚拟机-----
// JVM的核心功能:把Java的代码,翻译成CPU能够识别的机器指令.
// 缺点:(低效的) -> (像C/C++/Go,编译出来的程序就是CPU指令构成的程序,高效的)
// 优点:(更好的做到"跨平台"和"硬件无关","Write Once,Run Anywhere"(一次编写,到处运行))
// 原因:官方提供了多个版本的JVM(windows x86,linux x86等)
//     这些JVM对上层(Java程序)都是统一要求的,程序员只需要写一套代码,就可兼容以上环境~
// 跨平台: 同一个程序,可以放到不同的操作系统上运行.
//      : 同一个程序,可以放到不同架构的cpu上运行.

// -----Java程序是怎么运行的?-----
// 1.编写代码 (.java文本文件)
// 2.通过javac(命令行工具),把(.java)编译成(.class)文件(字节码文件,二进制)
//   (.java中写的每个class都对应一个.class文件,一个.java中允许有多个class)
// 3.通过java(命令行工具),运行对应的.class文件,这个java进程就可以理解成"一个Java虚拟机"
//   也可以称为JVM的进程,JVM的进展就会对.class文件中的内容解释执行了.

// -----JVM内存区域划分-----
// java程序跑起来得到java进程,需要从操作系统申请一大块内存空间.
// java进程就需要把这一大块空间分成多个区域,分别赋予不同的功能.
// 1.程序计数器:很小的空间,只需要保存一个"地址"(描述当前Java程序要运行的下个字节码指令的位置)
// (一个Java进程中需要有多个程序计数器,一个线程都需要一个程序计数器来记录)
// (程序计数器的值,java代码干预不了,处在"元数据区")
// 2.元数据区:用于存放"类"对应的指令
// java代码中,会为了创建对象而创建类(比如需要一个"正方体",就要创建"正方体类")
// 而"对象"需要内存空间保存,"类"也不例外,需要在内存空间保存.
// (JVM中,通过特殊的对象表示一个类的基本信息 -> (类名,类的父类,实现的接口,属性,方法...))
// (类对象在JVM进程中是"单例"的,JVM自身负责控制.)(元数据区的内容由"代码中写了多少类"决定)
// 3.栈:栈区域保存了方法调用关系.
// (Java的方法保证了,当前方法执行完毕,返回上一层调用位置继续执行)
// (该栈也具有"后进先出"的特点,并且JVM的栈中的每个元素,被称为"栈帧"(frame))
//  栈帧:表示一次的"方法调用"
//  1)方法的参数是哪些
//  2)方法中的局部变量有哪些
//  3)方法执行结束后,返回值结果
//  4)方法执行结束后,跳转回的地址
// (栈可能有多个,每个线程都要有一个.)(不同的栈帧里存着对应线程中的变量等信息)
// 4.堆:存放了new出来的对象(对象对应的内存空间在堆中)
//
// -----区域内存分配与占比-----
// class Test{
//     private int n;
//     private static int m;
//
//     public void test1(){
//     }
//     static public void test2(){
//     }
// }
// main() {
//     Test t = new Test();
// }
// 元数据区:(Test.class)(静态成员m)(方法test1的指令)(方法test2的指令)
// 栈:(局部变量t)
// 堆:(new Test())(非静态成员"实例属性")
// 整个进程中,区域的内存占比:
// 1)堆:最大的,远远超出其他区域(可以达到几十个G,只要机器内存够大,理论上可以非常大)
// 2)栈:比较小,取决于代码有多少个线程,每个线程调用方法的层次.
// 3)元数据区:大小中等,取决于代码规模(类的多少,每个类的方法和属性的多少)
// 4)程序计数器:最小,空间大小固定(8个字节),取决于有几个线程
//
// -----类加载的过程-----
// 类加载:JVM从最开始的读取.class文件,到最终构造完成"类对象"的整个过程
// (把类从"硬盘"加载到"内存"中)
// 步骤1:加载
//  1)根据代码中编写的"全限定类名"找到.class文件.(找的过程是"双亲委派模型")
//  2)打开文件,读取文件内容到内存中
//  3)数据格式的解析
//  全限定类名:包名+类名.如com.bit.Test , java.long.String
// 步骤2:验证
// 校验上述的.class文件读出来的内容是否合法
// (假设拿图片的二进制文件,后缀名改为.class,验证过程中发现格式问题,就能及时报错)
// 步骤3:准备
// 给类对象分配内存空间(申请的是"未初始化"的内存空间,每个字节都是"全0")
// 步骤4:解析
// 针对代码中的常量进行初始化,.class文件中,也会涉及到一些常量,需要放到内存里.
// 步骤5:初始化(用户写代码的环节)
//
// -----一个类,何时触发"加载"?-----
// "懒汉模式":某个类,直到第一次用到,才真正触发加载.
// "用到一个类":
// 1)构造某个类的实例
// 2)调用类的静态方法/使用类的静态成员
// 3)使用子类时,也会触发父类加载
//
// -----"双亲委派模型"-----
// JVM中进行类加载的操作,需要依赖内部的模块,"类加载器"(class loader)
// JVM自带了三种类加载器:
// 1)Bootstrap ClassLoader 负责在Java的标准库中进行查找
// 2)Extension ClassLoader 负责在Java的扩展库中进行查找
// 3)Application ClassLoader 负责在Java的第三方库/当前项目进行查找
// 这三个类加载器之间,存在"父子关系"(每个类加载器中有一个parent属性,保存父亲)
// 爷爷:Bootstrap ClassLoader
// 父亲:Extension ClassLoader
// 儿子:Application ClassLoader
// 比如此时加载一个"com.bit.Test"(自己项目中的类)
//      Bootstrap ClassLoader(开始查找)
//      ↑                  ↓
//      Extension ClassLoader
//      ↑                  ↓
// (入口)Application ClassLoader(找到,返回)
// (如果都没找到,报错)
// 双亲委派模型的作用:(保证类加载的优先级)
// 比如自定义了一个(Java.long.String),使用这个类时
// 实际加载的类是"JDK自带的",而非"自定义的"~
//
// -----JVM的垃圾回收机制GC-----
// GC会影响执行效率.
// 触发GC的时候,可能会涉及到STW问题(stop the world)"世界都停止"
// GC操作进行时,可能导致其他操作"暂时静止"
// 1.GC挥手的内存区域是哪个部分呢?
// 答:堆
// 2.GC的目的是为了释放内存,是以字节为单位"释放"吗?
// 答:不是,以"对象"为单位
// 3.如何回收?
//  1)找出垃圾(后续代码不再使用)
//  2)释放这些垃圾对象的内存
// -----如何找出垃圾?-----
// 答:如果一个对象已经没有任何引用指向它了,此时这个就对象注定无法被使用了.
// 判定一个对象是否为垃圾 -> 判定是否有引用指向这个对象
// 1.引用计数(Java没有使用,Python等采用)
// 给每个对象都分配了一个"计数器",当引用计数为0时,此时对象就没有引用指向,就成了"垃圾"
// 缺点:
//  1)消耗额外的内存空间较大
//  2)循环引用问题
// 2.可达性分析(Java使用的方法)
// 假设此时有一颗二叉树,当我们拥有root时,那么可以通过root访问到的一些子节点都有用
// 但当某一时刻,我们将(一个节点=null),那么这个节点以及"需要通过它找到的子节点"就都成了"垃圾"
//
// -----如何释放垃圾?-----
// 1.标记-清除(不是好方案)
// 标记:通过"可达性分析"查找垃圾
// 清除:直接释放垃圾的内存(free等方法)
// 问题:内存碎片问题
// (总的空闲内存空间比较多,但不连续,再次申请内存时(较大),可能就会失败,因为没有这么大的连续空间)
// 2.复制算法
// 把"不是垃圾"的对象,复制到另外一侧,就可以将原侧的整个空间都释放掉
// 问题:(内存浪费比较多)(复制存在开销)
// 3.标记-整理
// 类似于顺序表删除元素,"不是垃圾"的向前搬运
// 问题:(搬运存在开销)
// 4.分代回收(综合)
// 整个"堆空间",分成"新生代","老年代"(JVM不停使用线程扫描对象是否为垃圾,不是一次,年龄+1)
// 对于年轻对象:认为容易成为垃圾
// 对于老年对象:认为不容易成为垃圾
// 新生代:(伊甸区)|(幸存区|幸存区) (每涨一岁,对象从一个幸存区复制到另一个幸存区)
// (对象活过一轮GC,进入幸存区(复制算法))
// 老年代:达到一定年龄,存入老年代
// minor GC(新生代.高频)
// major GC(老年代.低频)
// Full GC
//
//
public class Demo1 {

}
